/*
 * Kernel dynamic hooks based on ftrace
 * aurelianliu@tencent.com
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <generated/utsrelease.h>
#include <asm/cacheflush.h>
#include <linux/ftrace.h>
#include <linux/string.h>

#include "version.h"
#include "hook.h"
#include "data_aware.h"
#include "kprobe_prehook.h"

#ifdef CONFIG_FUNCTION_TRACER
#define CC_USING_FENTRY
#endif

DEFINE_MUTEX(hook_func);
struct ftrace_verify_func
{
	char *modname;
	struct module *mod;
	char name[NAME_MAX];
	unsigned long old_addr;
	unsigned long old_offset;
	unsigned long new_addr;
	unsigned long new_offset;
};

struct ftrace_ksym
{
	int type;
	char name[NAME_MAX];
	void **address;
};

enum ksym_type {
	KSYM_DEF,
	KSYM_JMP_MCOUNT
};

struct percpu_counter ftrace_patch_num;
char ftrace_hook_name[NAME_MAX];
struct work_struct ftrace_work;
struct work_struct ftrace_work_init;
unsigned long *stat_one_func_time = NULL;
unsigned long *stat_one_func_num = NULL;
unsigned long stat_one_func_start_array[HOOK_FUNC_NUM];
unsigned long stat_one_func_total_array[HOOK_FUNC_NUM];
unsigned long stat_one_func_num_array[HOOK_FUNC_NUM];
unsigned long stat_func_total_time[HOOK_FUNC_NUM];
unsigned long stat_func_total_num[HOOK_FUNC_NUM];
unsigned long stat_total_num[HOOK_FUNC_NUM];
unsigned long stat_one_func_size = 0;
unsigned long stat_one_func_size_new = 0;
unsigned long stat_one_func_item_size = 0;
unsigned long (*p__test_func)(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4,
							  unsigned long arg5, unsigned long arg6);
#define __test_func(arg1, arg2, arg3, arg4, arg5, arg6) p__test_func(arg1, arg2, arg3, arg4, arg5, arg6)

struct open_flags {
    int open_flag;
    umode_t mode;
    int acc_mode;
    int intent;
    int lookup_flags;
};

char func_pointer[NAME_MAX];
char func_pointer_name[NAME_MAX];
char printk_name_first[NAME_MAX];
char printk_name_last[NAME_MAX];
static int hook_count;
unsigned long test_func(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4,
						unsigned long arg5, unsigned long arg6)
{
	ssize_t ret;
	struct bio *bio;
	struct blkdev_dio *dio;

	percpu_counter_inc(&ftrace_patch_num);
	print_func_name((void *)arg1, (void *)arg2, (void *)arg3, func_pointer, func_pointer_name);
	print_info((void *)arg1, (void *)arg2, (void *)arg3);

	stat_func_enter(NULL);
	ret = __test_func(arg1, arg2, arg3, arg4, arg5, arg6);
	stat_func_exit(NULL);

	percpu_counter_dec(&ftrace_patch_num);

	return ret;
}
void save_start_time(int index)
{
	int i, j;
	int cpu = smp_processor_id();

	percpu_counter_inc(&ftrace_patch_num);

	if (index < 1 || !module_is_enable())
	    goto out;

	i = stat_one_func_start_array[index];
	j = stat_one_func_num_array[index];
	stat_one_func_time[i + cpu] = sched_clock();
	stat_one_func_time[j + cpu]++;
	//if (index == 1)
	 // pr_info("index:%d, start:%d, num:%d, cpu:%d, time:%ld, %ld, func:%s, %d", index, i, j, cpu, stat_one_func_time[i + cpu],
	//	stat_one_func_time[j + cpu], get_one_func_name(index), hook_count);
	current->numa_migrate_retry |= 1 << index;
out:
	percpu_counter_dec(&ftrace_patch_num);
}
void save_total_time(int index)
{
	int i, j;
	int cpu = smp_processor_id();
	//char name[256];

	percpu_counter_inc(&ftrace_patch_num);

	if (index < 1 || !module_is_enable())
	    goto out;

	i = stat_one_func_start_array[index];
	j = stat_one_func_total_array[index];
	if (stat_one_func_time[i + cpu] > 0)
	    stat_one_func_time[j + cpu] += sched_clock() - stat_one_func_time[i + cpu];
#if 0
	if (hook_count > 1)
	  strcpy(name, get_one_func_name(index));
	if (strstr(name, "__vfs_write") && sched_clock() - stat_one_func_time[i + cpu] > 1000000)
	    pr_info("index:%d, start:%d, num:%d, cpu:%d, time:%ld, %ld, %ld, func:%s, %d", index, i, j, cpu, stat_one_func_time[i + cpu],
		sched_clock() - stat_one_func_time[i + cpu], stat_one_func_time[j + cpu], get_one_func_name(index), hook_count);
#endif
	current->numa_migrate_retry ^= 1 << index;
out:
	percpu_counter_dec(&ftrace_patch_num);
}
void save_sched_in(void)
{
	int i, j, bit;
	int cpu = smp_processor_id();
	unsigned long time;

	percpu_counter_inc(&ftrace_patch_num);

	if (hook_count <= 0)
	  goto out;

	time = sched_clock();
	for_each_set_bit(bit, &current->numa_migrate_retry, hook_count) {
	      i = stat_one_func_start_array[bit];
	      stat_one_func_time[i + cpu] = time;
	}
out:
	percpu_counter_dec(&ftrace_patch_num);
}
void save_sched_out(void)
{
	int i, j, bit;
	int cpu = smp_processor_id();
	unsigned long time;
	//char name[256];

	percpu_counter_inc(&ftrace_patch_num);

	if (hook_count <= 0)
	  goto out;

	time = sched_clock();
	for_each_set_bit(bit, &current->numa_migrate_retry, hook_count) {
	      i = stat_one_func_start_array[bit];
	      j = stat_one_func_total_array[bit];
	      if (time > stat_one_func_time[i + cpu] && stat_one_func_time[i + cpu] != 0)
		  stat_one_func_time[j + cpu] += time - stat_one_func_time[i + cpu];
#if 0
	      if (hook_count > 1)
		strcpy(name, get_one_func_name(bit));
	      if (strstr(name, "__vfs_write") && sched_clock() - stat_one_func_time[i + cpu] > 1000000)
		  pr_info("index:%d, start:%d, num:%d, cpu:%d, time:%ld, %ld, %ld, func:%s, %d", bit, i, j, cpu, stat_one_func_time[i + cpu],
		      sched_clock() - stat_one_func_time[i + cpu], stat_one_func_time[j + cpu], get_one_func_name(bit), hook_count);
#endif
	      stat_one_func_time[i + cpu] = 0;
	}
out:
	percpu_counter_dec(&ftrace_patch_num);
}
void stat_total_time(void)
{
	int i, j, k, cpu;

	if (hook_count <= 0)
	      return;

	for(i = 1; i <= hook_count; i++) {
	    j = stat_one_func_total_array[i];
	    k = stat_one_func_num_array[i];
	    stat_func_total_time[i] = 0;
	    stat_func_total_num[i] = 0;
	    for_each_possible_cpu(cpu) {
		stat_func_total_time[i] += stat_one_func_time[j + cpu];
		stat_func_total_num[i] += stat_one_func_time[k + cpu];
	    }
	    if (stat_func_total_num[i])
		stat_func_total_time[i] /= stat_func_total_num[i];
	}
}
unsigned long print_before_1(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4,
				 unsigned long arg5, unsigned long arg6)
{
	save_start_time(1);
	return 0;

}
void print_after_1(ssize_t ret, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4,
				 unsigned long arg5, unsigned long arg6)
{
	save_total_time(1);
}
unsigned long print_before_2(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4,
				 unsigned long arg5, unsigned long arg6)
{
	save_start_time(2);
	return 0;
};
void print_after_2(ssize_t ret, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4,
				 unsigned long arg5, unsigned long arg6)
{
	save_total_time(2);
};
unsigned long print_before_3(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4,
				 unsigned long arg5, unsigned long arg6)
{
	save_start_time(3);
	return 0;
};
void print_after_3(ssize_t ret, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4,
				 unsigned long arg5, unsigned long arg6)
{
	save_total_time(3);
};

unsigned long print_before_4(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4,
				 unsigned long arg5, unsigned long arg6)
{
	save_start_time(4);
	return 0;
};
void print_after_4(ssize_t ret, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4,
				 unsigned long arg5, unsigned long arg6)
{
	save_total_time(4);
};
unsigned long print_before_5(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4,
				 unsigned long arg5, unsigned long arg6)
{
	save_start_time(5);
	return 0;
};
void print_after_5(ssize_t ret, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4,
				 unsigned long arg5, unsigned long arg6)
{
	save_total_time(5);
};

#define DEFINE_TEST_PRINT(name) \
unsigned long print_before_##name(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, \
				 unsigned long arg5, unsigned long arg6) \
{ \
	save_start_time(name); \
	return 0; \
};\
\
void print_after_##name(ssize_t ret, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, \
				 unsigned long arg5, unsigned long arg6) \
{ \
	save_total_time(name); \
};

#define DEFINE_TEST(name) \
unsigned long (*p__test_func##name)(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, \
							  unsigned long arg5, unsigned long arg6); \
unsigned long test_func##name(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, \
						unsigned long arg5, unsigned long arg6)  \
{\
	ssize_t ret; \
	unsigned long test; \
\
        percpu_counter_inc(&ftrace_patch_num); \
\
	test = print_before_##name(arg1, arg2, arg3, arg4, arg5, arg6); \
\
	ret = p__test_func##name(arg1, arg2, arg3, arg4, arg5, arg6); \
\
	print_after_##name(ret, arg1, arg2, arg3, arg4, arg5, test); \
\
	percpu_counter_dec(&ftrace_patch_num); \
 \
	return ret; \
}

//HOOK_FUNC_NUM 50
DEFINE_TEST_PRINT(6)
DEFINE_TEST_PRINT(7)
DEFINE_TEST_PRINT(8)
DEFINE_TEST_PRINT(9)
DEFINE_TEST_PRINT(10)
DEFINE_TEST_PRINT(11)
DEFINE_TEST_PRINT(12)
DEFINE_TEST_PRINT(13)
DEFINE_TEST_PRINT(14)
DEFINE_TEST_PRINT(15)
DEFINE_TEST_PRINT(16)
DEFINE_TEST_PRINT(17)
DEFINE_TEST_PRINT(18)
DEFINE_TEST_PRINT(19)
DEFINE_TEST_PRINT(20)
DEFINE_TEST_PRINT(21)
DEFINE_TEST_PRINT(22)
DEFINE_TEST_PRINT(23)
DEFINE_TEST_PRINT(24)
DEFINE_TEST_PRINT(25)
DEFINE_TEST_PRINT(26)
DEFINE_TEST_PRINT(27)
DEFINE_TEST_PRINT(28)
DEFINE_TEST_PRINT(29)
DEFINE_TEST_PRINT(30)
DEFINE_TEST_PRINT(31)
DEFINE_TEST_PRINT(32)
DEFINE_TEST_PRINT(33)
DEFINE_TEST_PRINT(34)
DEFINE_TEST_PRINT(35)
DEFINE_TEST_PRINT(36)
DEFINE_TEST_PRINT(37)
DEFINE_TEST_PRINT(38)
DEFINE_TEST_PRINT(39)
DEFINE_TEST_PRINT(40)
DEFINE_TEST_PRINT(41)
DEFINE_TEST_PRINT(42)
DEFINE_TEST_PRINT(43)
DEFINE_TEST_PRINT(44)
DEFINE_TEST_PRINT(45)
DEFINE_TEST_PRINT(46)
DEFINE_TEST_PRINT(47)
DEFINE_TEST_PRINT(48)
DEFINE_TEST_PRINT(49)
DEFINE_TEST_PRINT(50)


DEFINE_TEST(1)
DEFINE_TEST(2)
DEFINE_TEST(3)
DEFINE_TEST(4)
DEFINE_TEST(5)
DEFINE_TEST(6)
DEFINE_TEST(7)
DEFINE_TEST(8)
DEFINE_TEST(9)
DEFINE_TEST(10)
DEFINE_TEST(11)
DEFINE_TEST(12)
DEFINE_TEST(13)
DEFINE_TEST(14)
DEFINE_TEST(15)
DEFINE_TEST(16)
DEFINE_TEST(17)
DEFINE_TEST(18)
DEFINE_TEST(19)
DEFINE_TEST(20)
DEFINE_TEST(21)
DEFINE_TEST(22)
DEFINE_TEST(23)
DEFINE_TEST(24)
DEFINE_TEST(25)
DEFINE_TEST(26)
DEFINE_TEST(27)
DEFINE_TEST(28)
DEFINE_TEST(29)
DEFINE_TEST(30)
DEFINE_TEST(31)
DEFINE_TEST(32)
DEFINE_TEST(33)
DEFINE_TEST(34)
DEFINE_TEST(35)
DEFINE_TEST(36)
DEFINE_TEST(37)
DEFINE_TEST(38)
DEFINE_TEST(39)
DEFINE_TEST(40)
DEFINE_TEST(41)
DEFINE_TEST(42)
DEFINE_TEST(43)
DEFINE_TEST(44)
DEFINE_TEST(45)
DEFINE_TEST(46)
DEFINE_TEST(47)
DEFINE_TEST(48)
DEFINE_TEST(49)
DEFINE_TEST(50)

static int hook_index[HOOK_FUNC_NUM];
/*
 * ksyms init
 */
static struct ftrace_ksym syms[] = {
	{KSYM_JMP_MCOUNT, "vfs_read", (void **)&p__test_func},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func1},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func2},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func3},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func4},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func5},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func6},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func7},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func8},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func9},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func10},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func11},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func12},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func13},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func14},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func15},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func16},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func17},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func18},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func19},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func20},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func21},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func22},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func23},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func24},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func25},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func26},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func27},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func28},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func29},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func30},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func31},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func32},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func33},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func34},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func35},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func36},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func37},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func38},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func39},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func40},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func41},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func42},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func43},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func44},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func45},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func46},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func47},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func48},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func49},
	{KSYM_JMP_MCOUNT, "p__test_func_tmp", (void **)&p__test_func50},
};

int ftrace_ksyms_init(void)
{
	unsigned long addr;
	int i = 0;
	int count = ARRAY_SIZE(syms);

	/* Init kernel symbols */
	while (true) {
		if (i >= count || strstr(syms[i].name, "p__test_func_tmp"))
			break;

#ifdef TK5
		addr = kallsyms_lookup_name_tk5(syms[i].name);
#else
		addr = kallsyms_lookup_name(syms[i].name);
#endif
		if (!addr) {
			pr_err("symbol %s not found, %d\n", syms[i].name, i);

			if (hook_count > 0) {
			    i++;
			    continue;
			}

			return -ENODEV;
		}
		pr_info("ksyms:symbol %s found, %d, %lx\n", syms[i].name, i, addr);

		switch (syms[i].type) {
		case KSYM_DEF:
			syms[i++].address[0] = (void *)addr;
			break;
		case KSYM_JMP_MCOUNT:
			syms[i++].address[0] = (void *)(addr + MCOUNT_INSN_SIZE);
			break;
		default:
			pr_err("symbol %s invalid type %d\n", syms[i].name, syms[i].type);
			return -EINVAL;
		}
	}
	return 0;
}

/*
 * Patched functions
 */
#ifdef CC_USING_FENTRY
static struct ftrace_verify_func verify_funcs[] =
{
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func1, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func2, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func3, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func4, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func5, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func6, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func7, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func8, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func9, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func10, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func11, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func12, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func13, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func14, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func15, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func16, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func17, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func18, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func19, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func20, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func21, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func22, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func23, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func24, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func25, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func26, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func27, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func28, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func29, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func30, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func31, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func32, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func33, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func34, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func35, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func36, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func37, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func38, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func39, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func40, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func41, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func42, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func43, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func44, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func45, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func46, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func47, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func48, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func49, 0},
	{NULL, NULL, "test_func_tmp", 0, 0, (unsigned long)test_func50, 0},
};
	//{NULL, NULL, "do_syscall_64", 0, 0, (unsigned long)stat_do_syscall_64, 0},

#else

#error "error, compiler donot support fentry?";

#endif

bool check_func_name(char *func)
{
	return strstr(verify_funcs[0].name, func);
}

char *get_one_func_name(int i)
{
    if (i > hook_count)
      return "NULL";
    return verify_funcs[i].name;
}
static struct module *get_link_module(char *modname)
{
	struct module *mod;

#ifdef TK5
	mutex_lock(module_mutex_tk5);
	mod = find_module_tk5(modname);
#else
	mutex_lock(&module_mutex);
	mod = find_module(modname);
#endif
	if (!mod) {
#ifdef TK5
		mutex_unlock(module_mutex_tk5);
#else
		mutex_unlock(&module_mutex);
#endif
		return NULL;
	}

	WARN_ON(!try_module_get(mod));
#ifdef TK5
	mutex_unlock(module_mutex_tk5);
#else
	mutex_unlock(&module_mutex);
#endif

	return mod;
}

static inline void put_link_modules(struct module *mod)
{
	if (mod)
		module_put(mod);
}

static int verify_kernel(void)
{
	int i, count = ARRAY_SIZE(verify_funcs);
	char name[256];

	for (i = 0; i < count; i++) {
		if (verify_funcs[i].modname) {
			verify_funcs[i].mod = get_link_module(verify_funcs[i].modname);
			if (!verify_funcs[i].mod){
				pr_err("unable to find module '%s'\n", verify_funcs[i].modname);
				return -ENXIO;
			}
			snprintf(name, sizeof(name), "%s:%s",verify_funcs[i].modname, verify_funcs[i].name);
		} else {
			if(strstr(verify_funcs[i].name, "test_func_tmp"))
			    continue;
			strcpy(name, verify_funcs[i].name);
		}

		/* check symbol */
#ifdef TK5
		verify_funcs[i].old_addr = kallsyms_lookup_name_tk5(name);
#else
		verify_funcs[i].old_addr = kallsyms_lookup_name(name);
#endif
		if (!verify_funcs[i].old_addr) {
			pr_err("unable to find symbol '%s'\n", name);
			if (hook_count > 0) {
			    strcpy(verify_funcs[i].name, "test_func_tmp");
			    continue;
			}
			return  -ENXIO;
		}
		pr_info("find symbol '%s', %lx\n", name, verify_funcs[i].old_addr);
	}
	return 0;
}

static struct ftrace_verify_func *tpatch_find_match_ip(unsigned long ip)
{
	int count = ARRAY_SIZE(verify_funcs);
	int i;

	for ( i =0; i < count ; i++) {
		if (ip == verify_funcs[i].old_addr + verify_funcs[i].old_offset) {
			return &verify_funcs[i];
		}
	}

	return NULL;
}

/* Update regs->ip to tell ftrace to return
 * to the new function.*/
#ifdef TK5
static void notrace tpatch_ftrace_handler(unsigned long ip, unsigned long parent_ip,
		      struct ftrace_ops *fops, struct ftrace_regs *regs)
#else
static void notrace tpatch_ftrace_handler(unsigned long ip, unsigned long parent_ip,
		      struct ftrace_ops *fops, struct pt_regs *regs)
#endif
{
	struct ftrace_verify_func *func;

	preempt_disable_notrace();

#ifdef CONFIG_ARM64
	/*
	 * In arm64, the compiler has inserted two NOPs before the regular
	 * function prologue.
	 *
	 * At runtime we want to be able to swing a single NOP <-> BL to enable or
	 * disable the ftrace call. The BL requires us to save the original LR value,
	 * so here ftrace insert a <MOV X9, LR> over the first NOP so the instructions
	 * before the regular prologue are:
	 *
	 * | Compiled | Disabled   | Enabled    |
	 * +----------+------------+------------+
	 * | NOP      | MOV X9, LR | MOV X9, LR |
	 * | NOP      | NOP        | BL <entry> | <== this is ip/pc actually position.
	 *
	 * When ftrace call into this fuction, ip/pc is actually at the second
	 * instruction. Therefore, we need to decrease ip/pc with 1 * AARCH64_INSN_SIZE
	 * to get correct function address.
	 */
	ip -= AARCH64_INSN_SIZE;
#endif

	func = tpatch_find_match_ip(ip);
	if (func)
#ifdef CONFIG_X86
#ifdef TK5
		regs->regs.ip = func->new_addr + MCOUNT_INSN_SIZE;
#else
		regs->ip = func->new_addr + MCOUNT_INSN_SIZE;
#endif
#elif defined(CONFIG_ARM64)
#ifdef TK5
		regs->regs.pc = func->new_addr;
#else
		regs->pc = func->new_addr;
#endif
#endif

	preempt_enable_notrace();
}

static struct ftrace_ops tpatch_ftrace_ops __read_mostly = {
	.func = tpatch_ftrace_handler,
	.flags = FTRACE_OPS_FL_SAVE_REGS,
};

static int patch_kernel(void)
{
	int count = ARRAY_SIZE(verify_funcs);
	int ret, i = 0;

	for ( i =0; i < count ; i++) {
		if(strstr(verify_funcs[i].name, "test_func_tmp"))
		    goto put_module;
		ret = ftrace_set_filter(&tpatch_ftrace_ops, verify_funcs[i].name, strlen(verify_funcs[i].name), 0);
		if (ret < 0) {
			pr_err("can't set ftrace filter func:%s at address 0x%lx, ret(%d)\n", verify_funcs[i].name,
			       verify_funcs[i].old_addr, ret);
			if (hook_count > 0) {
			      hook_index[i] = i;
			      continue;
			}
			goto error;
		}
		hook_index[i] = 0;

put_module:
		/* put module */
		put_link_modules(verify_funcs[i].mod);
	}

	ret = register_ftrace_function(&tpatch_ftrace_ops);
	if (ret < 0) {
		pr_err("can't register ftrace handler\n");
		goto error;
	}

	return 0;

 error:
	for (; i < count; i++)
		put_link_modules(verify_funcs[i].mod);

	return ret;
}

int ftrace_patch_init(char *name)
{
	int ret, i;

	if (!name) {
		pr_err("name null\n");
		return -ENODEV;
	}

	mutex_lock(&hook_func);
	if (register_ftrace_ftrace == 3) {
		while (register_ftrace_ftrace == 3)
		      schedule_timeout(1000);
	}

	if (register_ftrace_ftrace == 2) {
		mutex_unlock(&hook_func);
		pr_err("need unregister ftrace hook function:maybe:%s; or: %s", ftrace_hook_name, symbol_new);
		return -EPERM;
	}
	/* reuses the pointer */
	stat_one_func_item_size = nr_cpu_ids * sizeof(unsigned long);
	stat_one_func_size_new = (hook_count + 1) * 3 * stat_one_func_item_size;
	if ((hook_count > 0) && (stat_one_func_time == NULL ||  stat_one_func_size_new > stat_one_func_size)) {
	    stat_one_func_size = stat_one_func_size_new;
	    if (stat_one_func_time)
		  vfree(stat_one_func_time);
	    stat_one_func_time = (unsigned long *)vzalloc(stat_one_func_size);
	    for (i = 0; i <= hook_count; i++) {
		stat_one_func_start_array[i] = (i * 3) * nr_cpu_ids;
		stat_one_func_total_array[i] = stat_one_func_start_array[i] + nr_cpu_ids;
		stat_one_func_num_array[i] = stat_one_func_total_array[i] + nr_cpu_ids;
	    }
	}

	pr_info("%s, %s, %s, %d", __func__, verify_funcs[hook_count].name, name, hook_count);

	strcpy(verify_funcs[0].name, name);
	strcpy(syms[0].name, name);

	ftrace_ksyms_init();
	/* Verify patched functions */
	ret = verify_kernel();
	if(ret < 0) {
		mutex_unlock(&hook_func);
		pr_err("Incorrect kernel, or function not found\n");
		return -ENODEV;
	}

#if defined(TK2) && !defined(KVM3)
	percpu_counter_init(&ftrace_patch_num, 0);
#else
	percpu_counter_init(&ftrace_patch_num, 0, GFP_KERNEL);
#endif
	/* Ok, try to replace target functions */
	ret = patch_kernel();
	if (ret < 0) {
		percpu_counter_destroy(&ftrace_patch_num);
		mutex_unlock(&hook_func);
		return ret;
	}
	for (i = 0; i < HOOK_FUNC_NUM; i++) {
	    if (hook_index[i] == 0)
	      continue;
	    ret = register_kret_new_func_batch(i, verify_funcs[i].name);
	    if (ret < 0)
		hook_index[i] = 0;
	}

	register_ftrace_ftrace = 2;
	mutex_unlock(&hook_func);
	return 0;
}

void ftrace_patch_exit(void)
{
	int count = ARRAY_SIZE(verify_funcs) - 1, i;
	int count_syms = ARRAY_SIZE(syms) - 1;

	if (register_ftrace_ftrace != 3)
		goto out;

	/* Destroy ftrace filter */
	unregister_ftrace_function(&tpatch_ftrace_ops);
	synchronize_rcu();

	/* Wait all exit patched function */
	while (percpu_counter_sum(&ftrace_patch_num))
		msleep(1);

	percpu_counter_destroy(&ftrace_patch_num);

	for (i = 0; i < HOOK_FUNC_NUM; i++) {
	    if (hook_index[i] == 0)
	      continue;
	    unregister_kret_new_func_batch(i);
	}

	for (i = 1; i <= count; i++)
	    strcpy(verify_funcs[i].name, "test_func_tmp");
	for (i = 1; i <= count_syms; i++)
	    strcpy(syms[i].name, "p__test_func_tmp");
	for (i = 0; i <= hook_count; i++)
		hook_index[i] = 0;

	hook_count = 0;
	register_ftrace_ftrace = 0;

out:
	return;
}

int sysctl_ftrace_hook_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos)
{
	int ret;

	if (write && !capable(CAP_SYS_ADMIN))
		return -EPERM;

	if (sysctl_module_disable)
		return -EPERM;

	ret = proc_dostring(table, write, buffer, lenp, ppos);

	if (!write)
		return ret;

	schedule_work_on(0, &ftrace_work_init);

	return 0;
}

int sysctl_ftrace_func_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos)
{
	int ret;
	int count = ARRAY_SIZE(verify_funcs) - 1;

	if (write && !capable(CAP_SYS_ADMIN))
		return -EPERM;

	if (sysctl_module_disable)
		return -EPERM;

	if (hook_count >= count)
		return -EPERM;

	ret = proc_dostring(table, write, buffer, lenp, ppos);

	if (!write)
		return ret;
	hook_count++;
	strcpy(verify_funcs[hook_count].name, ftrace_hook_name);
	strcpy(syms[hook_count].name, ftrace_hook_name);

	return 0;
}

void ftrace_hook_work_fn(struct work_struct *work)
{
	ftrace_patch_init(ftrace_hook_name);
	return;
}

void ftrace_unhook_work_fn(struct work_struct *work)
{
	int i;

	mutex_lock(&hook_func);

	if (register_ftrace_ftrace != 2)
		goto out;

	hook_count = 0;

	register_ftrace_ftrace = 3;
	ftrace_patch_exit();

out:
	mutex_unlock(&hook_func);

	return;
}
int sysctl_ftrace_unhook_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos)
{
	int ret = 0;

	if (write && !capable(CAP_SYS_ADMIN))
		return -EPERM;

	if (sysctl_module_disable)
		return -EPERM;

	ret = proc_dostring(table, write, buffer, lenp, ppos);

	if (!write)
		return ret;

	schedule_work_on(0, &ftrace_work);

	return ret;
}
